package CheckersApplet;

import java.util.ArrayList;

import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.CyclicBehaviour;
import jade.core.behaviours.OneShotBehaviour;
import jade.core.behaviours.TickerBehaviour;
import jade.core.behaviours.WakerBehaviour;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;

public class TournamentAgent extends Agent {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private AID[] players;
	private int numberOfPlayers = 2;
	private Boolean[] isPlayerPlaying;
	private ArrayList<PlayersPair> matchesToPlay;
	private int[] scores;
	private int numberOfMatches;
	private int numberOfFinishedMatches = 0;
	private final int WaitForPlayersInterval = 5000;
		
	protected void setup() {
		System.out.println("Hello! Tournament-agent " + getAID().getName()
				+ " is ready.");

		Object[] args = getArguments();
		if (args != null && args.length > 0) {
			numberOfPlayers = Integer.parseInt((String) args[0]);
		}

		isPlayerPlaying = new Boolean[numberOfPlayers];
		for (int i = 0; i < isPlayerPlaying.length ; i++){
			isPlayerPlaying[i] = false;
		}
		matchesToPlay = CombinationGenerator
				.getPairCombinations(numberOfPlayers);
		numberOfMatches = matchesToPlay.size(); 
		scores = new int[numberOfPlayers];
		
		addBehaviour(new WaitForPlayersBehaviour(this, WaitForPlayersInterval));
	}

	protected void takeDown() {
		System.out.println("Tournament-agent " + getAID().getName()
				+ " terminating.");
		super.takeDown();
	}

	private class PlayTournament extends CyclicBehaviour {

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		@Override
		public void action() {
			ACLMessage msg = myAgent.receive();
			if (msg != null){
				//System.out.println("Tournament: message received: " + msg.getContent());
				processMessage(msg);				
			}
			if (!matchesToPlay.isEmpty()) {
				for (int i = 0; i < players.length; i++) {
					int opponent = findOpponent(i);
					if (opponent == -1)
						continue;
					
					 ACLMessage replyMsg = new ACLMessage(ACLMessage.PROPOSE);
					 replyMsg.addReceiver(new AID(players[i].getLocalName(),
					 AID.ISLOCALNAME));
					 replyMsg.setContent(buildMsgContent(opponent, "red"));
					 System.out.println();
					 System.out.print(players[i].getLocalName() + " (red) vs. ");
					 send(replyMsg);
					 
					 replyMsg = new ACLMessage(ACLMessage.PROPOSE);
					 replyMsg.addReceiver(new AID(players[opponent].getLocalName(),
					 AID.ISLOCALNAME));
					 replyMsg.setContent(buildMsgContent(i, "black"));
					 System.out.print(players[opponent].getLocalName() + " (black)");
					 System.out.println();
					 send(replyMsg);
				}
			}
			else if (numberOfFinishedMatches == numberOfMatches * 2){//TODO: fix it
				System.out.println("Tournament if over!!!");
				System.out.println("Points:");
				for (int j = 0; j < numberOfPlayers ; j++){
					System.out.println(players[j].getLocalName() + ": " + String.valueOf(scores[j]));
				}
				//takeDown();	
				myAgent.removeBehaviour(this);
			}
		}

		private void processMessage(ACLMessage msg) {
			numberOfFinishedMatches++;
			for (int i = 0; i < numberOfPlayers; i++){
				if (players[i].getLocalName().equals(msg.getSender().getLocalName())){
					isPlayerPlaying[i] = false;
					scores[i] += Integer.valueOf(msg.getContent());
					System.out.println("Matches to play: " + String.valueOf(matchesToPlay.size()));
					System.out.println("Points:");
					for (int j = 0; j < numberOfPlayers ; j++){
						System.out.println(players[j].getLocalName() + ": " + String.valueOf(scores[j]));
					}				
					return;
				}					
			}
		}
	}	
	
	private class WaitForPlayersBehaviour extends WakerBehaviour {

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public WaitForPlayersBehaviour(Agent a, long timeout) {
			super(a, timeout);
		}

		protected void handleElapsedTimeout() {
			DFAgentDescription template = new DFAgentDescription();
			ServiceDescription sd = new ServiceDescription();
			sd.setType("checkers-player");
			template.addServices(sd);
			try {
				DFAgentDescription[] result = DFService.search(myAgent,
						template);
				players = new AID[result.length];
				System.out.println(players.length + " players registered!");
				for (int i = 0; i < result.length; ++i) {
					players[i] = result[i].getName();
					System.out.print(" " + players[i].getLocalName());
				}
				System.out.println();
			} catch (FIPAException fe) {
				fe.printStackTrace();
			}

			if (players.length >= numberOfPlayers)
				addBehaviour(new PlayTournament());
			else {
				System.out.println("Not enough players !!!");
				takeDown();
			}
		}
	}

	private String buildMsgContent(int i, String color) {
		String ret = "";
		ret = players[i].getLocalName();
		ret += " " + color;
		return ret;
	}

	private int findOpponent(int playerNumber) {
		//System.out.println("Player number to find opponent " + String.valueOf(playerNumber));
		if (isPlayerPlaying[playerNumber])
			return -1;

		for (int j = 0; j < matchesToPlay.size() ; j++){
			PlayersPair match = matchesToPlay.get(j);
			if (match.getFirst() == playerNumber || match.getSecond() == playerNumber){
				if (!isPlayerPlaying[match.getFirst()] && !isPlayerPlaying[match.getSecond()]){
					isPlayerPlaying[match.getFirst()] = true;
					isPlayerPlaying[match.getSecond()] = true;
					matchesToPlay.remove(j);
					if (match.getFirst() == playerNumber)
						return match.getSecond();
					else return match.getFirst();
				}
			}
		}
		return -1;
	}
}
